package ar.com.profertil.utils.service;

import java.io.BufferedInputStream;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;

import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

import ar.com.profertil.utils.Zipper;
import ar.com.profertil.utils.certificado.Base64;
import ar.com.profertil.utils.entity.Element;
import ar.com.profertil.utils.entity.RecibirArchivoEntity;
import ar.com.profertil.utils.exception.InvalidMD5Exception;
import ar.com.profertil.utils.parser.ParserIdoc;

/**
 * The Class RecibirArchivoBancoService.
 */
public class RecibirArchivoBancoService {

	/** The Constant TEMP_FILE. */
	private static final String TEMP_FILE = "FileDesEncriptedTemp.txt";
	
	/**
	 * Recibe un String del Banco y lo parsea a una Entidad.
	 *
	 * @param pathDirectorioTrabajo carpeta en el server donde se levanta y se guardan los archivos
	 * @param nombreCertificado el nombre del certificado
	 * @param data El String a parsear
	 * @param nombreArchivoDestino nombre del archivo de destino (dentro del directorio de trabajo)
	 * @param password con el que se genero el certificado
     * @param keyStorageView the key storage view
     * @param sAlias the alias of a key in the key storage view
	 * @param parser the parser
	 * @param useCSV falg true se guarda separado por comas, false en XML
	 * @param zipFile flag para comprimir el archivo en zip, true zippea, false no
	 * @return {@link RecibirArchivoEntity} Entidad a la que se parsea
	 * @throws Exception the exception
	 */
	public RecibirArchivoEntity recibirString(
								  String pathDirectorioTrabajo,
								  String nombreCertificado,
								  String data,
								  String nombreArchivoDestino,
								  String password,
						   		  String keyStorageView,
						   		  String sAlias,
								  ParserIdoc parser,
								  boolean useCSV,
								  boolean zipFile) throws Exception {
		
		//cargo los datos del certificado en memoria
		RSABC.obtenerDatosPublicos(null, keyStorageView, sAlias, password);
		//desencriptar los datos con las claves del certificado
		String doc = RSABC.desencriptarStringLargo(data, RSABC.getMod(), RSABC.getPubExp());
		//Parseas El contenido
		RecibirArchivoEntity recibirArchivoEntity = parser.armarIdocArchivoRecibido(doc);
		//Escribis el archivo de destino con el resultado
		writeFile(pathDirectorioTrabajo, nombreArchivoDestino, useCSV, zipFile, recibirArchivoEntity);
		
		return recibirArchivoEntity;
	}

	private void writeFile(String pathDirectorioTrabajo,
			String nombreArchivoDestino, boolean useCSV, boolean zipFile, RecibirArchivoEntity recibirArchivoEntity)
			throws UnsupportedEncodingException, FileNotFoundException,
			IOException, Exception {
		BufferedWriter fos = new BufferedWriter(
			       new OutputStreamWriter(
			                  new FileOutputStream(pathDirectorioTrabajo + nombreArchivoDestino), "UTF16"));
		if(useCSV) {
			for(Element element : recibirArchivoEntity.getElements()) {
				fos.write(element.toCSVString());
				fos.newLine();
			}
		} else {
			// aca se escribe en xml
			fos.write(recibirArchivoEntity.toString());
		}
		fos.close();

		if(zipFile) {
			Zipper.zip(pathDirectorioTrabajo + nombreArchivoDestino, pathDirectorioTrabajo + nombreArchivoDestino + ".zip");
		}
	}
	
	/**
	 * Recibir archivo.
	 *
	 * @param pathDirectorioTrabajo the path directorio trabajo
	 * @param nombreCertificado the nombre certificado
	 * @param nombreArchivoOrigen the nombre archivo origen
	 * @param nombreArchivoDestino the nombre archivo destino
	 * @param clave3Des the clave3 des
	 * @param password the password
     * @param keyStorageView the key storage view
     * @param sAlias the alias of a key in the key storage view
	 * @param firmaArchivo the firma archivo
	 * @param parser the parser
	 * @param useCSV the use csv
	 * @param zipFile the zip file
	 * @return the recibir archivo entity
	 * @throws Exception the exception
	 */
	public RecibirArchivoEntity recibirArchivo(
								  String pathDirectorioTrabajo,
								  String nombreCertificado,
								  String nombreArchivoOrigen,
								  String nombreArchivoDestino,
								  String clave3Des,
								  String password,
						   		  String keyStorageView,
						   		  String sAlias,
								  String firmaArchivo,
								  ParserIdoc parser,
								  boolean useCSV,
								  boolean zipFile) throws Exception {
		
		//desencripto la pass
		String clave3DesDesencriptada = RSABC.procesarStringPriv(false, null, keyStorageView, sAlias, password, clave3Des);
		
		Base64 b64 = new Base64();
		//decodifico la pass
		byte[] key = b64.decode(clave3DesDesencriptada);
		
		SecretKey secKey = new SecretKeySpec(key, "DESede");
		//Desencripto en 3pleDes (el false indica que desencripta por mas que el metodo se llame encriptar)
		TripleDES.encriptar(pathDirectorioTrabajo + nombreArchivoOrigen, pathDirectorioTrabajo + TEMP_FILE, secKey, false);
		String firmaDesencriptada = RSABC.procesarStringPriv(false, null, keyStorageView, sAlias, password, firmaArchivo);
		String firmaCalculada = RSABC.obtenerMD5(pathDirectorioTrabajo + TEMP_FILE); 
		
		if(firmaDesencriptada.equals(firmaCalculada)) {
			BufferedInputStream bis = new BufferedInputStream(
					new FileInputStream(
						new File(pathDirectorioTrabajo + TEMP_FILE)));
			
			byte[] data = new byte[bis.available()];
			bis.read(data);
			bis.close();
			
			new File(pathDirectorioTrabajo + TEMP_FILE).delete();
			
			String doc = new String(data);
			RecibirArchivoEntity recibirArchivoEntity = parser.armarIdocArchivoRecibido(doc);
			
			writeFile(pathDirectorioTrabajo, nombreArchivoDestino, useCSV,
					zipFile, recibirArchivoEntity);
			
			return recibirArchivoEntity;
		} else {
			throw new InvalidMD5Exception();
		}	
	}
	
	/**
	 * Recibir archivo.
	 *
	 * @param encripted the encripted string
	 * @param password the password
     * @param keyStorageView the key storage view
     * @param sAlias the alias of a key in the key storage view
	 * @param parser the parser
	 * @return the recibir archivo entity
	 * @throws Exception the exception
	 */
	public RecibirArchivoEntity recibirArchivo(
								  String encripted,
								  String password,
						   		  String keyStorageView,
						   		  String sAlias,
								  ParserIdoc parser) throws Exception {
		
		//cargo los datos del certificado en memoria
		RSABC.obtenerDatosPublicos(null, keyStorageView, sAlias, password);
		//desencriptar los datos con las claves del certificado
		String doc = RSABC.desencriptarStringLargo(encripted, RSABC.getMod(), RSABC.getPubExp());
		//Parseas El contenido
		RecibirArchivoEntity recibirArchivoEntity = parser.armarIdocArchivoRecibido(doc);
		
		return recibirArchivoEntity;
	}
}
